﻿using Aribaccio.Logger;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Aribaccio.Device
{
    /// <summary>
    /// 设备串行基类
    /// </summary>
    /// <typeparam name="TPara">设备串行连接参数接口</typeparam>
    public class DeviceSerial<TPara> : DeviceBase<TPara>, IDeviceSerial<TPara> where TPara : IDevicePara
    {
        /// <summary>
        /// 串行端口资源对象
        /// </summary>
        public SerialPort DeviceSerialPort { get; set; }
        /// <summary>
        /// 串口读取超时时间
        /// </summary>
        public int DefaultReadTimeout { get; set; }
        /// <summary>
        /// 串口写入超时时间
        /// </summary>
        public int DefaultWriteTimeout { get; set; }
        /// <summary>
        /// 睡眠的时间
        /// </summary>
        public int SleepTime { get; set; }
        /// <summary>
        /// 接收数据的超时时间/毫秒
        /// </summary>
        public int ReceiveTimeout { get; set; }
        /// <summary>
        /// 接收数据的超时次数
        /// </summary>
        public int ReceiveTimeoutNum { get; set; }

        /// <summary>
        /// 节点数据开始长度
        /// </summary>
        protected int NodeDataStartNum { get; set; }
        /// <summary>
        /// 节点数据结束长度
        /// </summary>
        protected int NodeDataEndNum { get; set; }
        /// <summary>
        /// 节点数据开始长度
        /// </summary>
        protected int NodeDataNum { get { return this.NodeDataStartNum + this.NodeDataEndNum; } }
        /// <summary>
        /// 是否自动通过事件接收数据
        /// </summary>
        public bool IsAutoReceivedMode { get; protected set; }

        /// <summary>
        /// 是否被打开
        /// </summary>
        public override bool IsOpen => this.DeviceSerialPort?.IsOpen ?? false;
        /// <summary>
        /// 设备地址端口
        /// </summary>
        public override string AddressPort => this.DeviceSerialPort?.PortName ?? string.Empty;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="argDevicePara">设备参数接口对象</param>
        public DeviceSerial(TPara argDevicePara)
            : base(argDevicePara)
        {
            this.DeviceSerialPort = new SerialPort();
            this.DefaultReadTimeout = 2000;
            this.DefaultWriteTimeout = 2000;
            this.ReceiveTimeout = 500;
            this.ReceiveTimeoutNum = 2;
            this.SleepTime = 10;
            this.NodeDataStartNum = 4;
            this.NodeDataEndNum = 2;
            this.UpdateSerialPara();
        }

        /// <summary>
        /// 更新串口参数
        /// </summary>
        public void UpdateSerialPara()
        {
            //再检查串口资源对象
            if (this.DeviceSerialPort == null) { this.DeviceSerialPort = new SerialPort(); }
            if (this.DevicePara != null && (this.DevicePara is IDeviceParaSerial deviceParaSerial))
            {
                this.IsAutoReceivedMode = deviceParaSerial.AutoMode;
                this.DeviceSerialPort.PortName = deviceParaSerial.PortName;
                this.DeviceSerialPort.BaudRate = deviceParaSerial.BaudRate;
                this.DeviceSerialPort.DataBits = deviceParaSerial.DataBits;
                this.DeviceSerialPort.StopBits = deviceParaSerial.StopBits;
                this.DeviceSerialPort.Parity = deviceParaSerial.ParityBits;
                this.DeviceSerialPort.ReadBufferSize = 4096;
                //需全局注册 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)
                //this.DeviceSerialPort.Encoding = Encoding.GetEncoding("GB2312");
                this.DeviceSerialPort.ReadTimeout = this.DefaultReadTimeout;
                this.DeviceSerialPort.WriteTimeout = this.DefaultWriteTimeout;
                this.DeviceSerialPort.DtrEnable = true;
                this.DeviceSerialPort.RtsEnable = true;
            }
        }

        /// <summary>
        /// 打开设备连接[重写]
        /// </summary>
        /// <returns>是否成功信息</returns>
        public override Result Open()
        {
            Result ResultData = new Result() { ResultCode = ResultStatus.Succeed };
            try
            {
                //开启之前先判断关闭
                this.Close();
                //先重新更新一下串口参数
                this.UpdateSerialPara();
                //再次检查是否开启，没有则启动
                if (this.DeviceSerialPort != null && !this.DeviceSerialPort.IsOpen)
                {
                    this.DeviceSerialPort.Open();
                    this.DeviceSerialPort.DiscardOutBuffer(); //清空传输缓冲区
                    this.DeviceSerialPort.DiscardInBuffer();  //清空接收缓冲区
                }
                //再次检查是否开启
                if (this.DeviceSerialPort != null && this.DeviceSerialPort.IsOpen)
                {
                    if (this.IsAutoReceivedMode)
                    {
                        this.DeviceSerialPort.DataReceived += new SerialDataReceivedEventHandler(RreadSerialPortData);
                    }
                    ResultData.ResultCode = ResultStatus.Succeed;
                    ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][打开串行端口]->成功！";
                }
                else
                {
                    ResultData.ResultCode = ResultStatus.Failure;
                    ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][打开串行端口]->失败！";
                }
            }
            catch (Exception ep)
            {
                ResultData.ResultCode = ResultStatus.Exception;
                ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][打开串行端口]->异常：" + ep.Message;
            }
            return ResultData;
        }

        /// <summary>
        /// 关闭设备连接[重写]
        /// </summary>
        /// <returns>是否成功信息</returns>
        public override Result Close()
        {
            Result ResultData = new Result() { ResultCode = ResultStatus.Succeed };
            try
            {
                if (this.DeviceSerialPort != null)
                {
                    if (this.DeviceSerialPort.IsOpen) { this.DeviceSerialPort.Close(); }
                    if (!this.DeviceSerialPort.IsOpen)
                    {
                        ResultData.ResultCode = ResultStatus.Succeed;
                        ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][关闭串行端口]->成功！";
                    }
                    else
                    {
                        ResultData.ResultCode = ResultStatus.Failure;
                        ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][关闭串行端口]->失败！";
                    }
                }
            }
            catch (Exception ep)
            {
                ResultData.ResultCode = ResultStatus.Exception;
                ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][关闭串行端口]->异常：" + ep.Message;
            }
            return ResultData;
        }


        /// <summary>
        /// 写串行端口数据
        /// </summary>
        /// <param name="argSerialPortData">串行端口数据</param>
        public Result SendSerialPortData(byte[] argSerialPortData)
        {
            Result ResultData = new Result() { ResultCode = ResultStatus.Succeed };
            try
            {
                if (this.DeviceSerialPort != null && this.DeviceSerialPort.IsOpen)
                {
                    this.DeviceSerialPort.DiscardOutBuffer(); //清空传输缓冲区
                    this.DeviceSerialPort.DiscardInBuffer();  //清空接收缓冲区                 
                    this.DeviceSerialPort.Write(argSerialPortData, 0, argSerialPortData.Length);
                }
                else
                {
                    ResultData.ResultCode = ResultStatus.Failure;
                    ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][写入串行端口数据]->失败：可能串行端口未创建或打开";
                }
            }
            catch (Exception ep)
            {
                ResultData.ResultCode = ResultStatus.Exception;
                ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][写入串行端口数据]->异常：" + ep.Message;
            }
            return ResultData;
        }

        /// <summary>
        /// 写串行端口数据
        /// </summary>
        /// <param name="argSerialPortData">串行端口数据</param>
        /// <param name="argIsEnter">是否多加换行符</param>
        public Result SendSerialPortData(string argSerialPortData, bool argIsEnter = true)
        {
            Result ResultData = new Result() { ResultCode = ResultStatus.Succeed };
            try
            {
                if (this.DeviceSerialPort != null && this.DeviceSerialPort.IsOpen)
                {
                    this.DeviceSerialPort.DiscardOutBuffer(); //清空传输缓冲区
                    this.DeviceSerialPort.DiscardInBuffer();  //清空接收缓冲区
                    if (argIsEnter)
                    {
                        this.DeviceSerialPort.WriteLine(argSerialPortData + "\r\n");
                    }
                    else
                    {
                        this.DeviceSerialPort.WriteLine(argSerialPortData);
                    }
                }
                else
                {
                    ResultData.ResultCode = ResultStatus.Failure;
                    ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][写串行端口数据]->失败：写入串行端口数据失败，可能串行端口未创建或打开";
                }
            }
            catch (Exception ep)
            {
                ResultData.ResultCode = ResultStatus.Exception;

                ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][写串行端口数据]->异常：" + ep.Message;
            }
            return ResultData;
        }

        public ResultModel<byte[]> RreadSerialPortData()
        {
            ResultModel<byte[]> ResultData = new ResultModel<byte[]>() { ResultCode = ResultStatus.Failure };
            MemoryStream ms = new MemoryStream();
            try
            {
                byte[] buffer = new byte[1024];
                //继续接收数据
                int sp_receive = this.DeviceSerialPort.Read(buffer, 0, buffer.Length);
                //存储到流当中
                ms.Write(buffer, 0, sp_receive);
                if (sp_receive > 0)
                {
                    ResultData.ResultCode = ResultStatus.Succeed;
                }
            }
            catch (Exception ep)
            {
                ms.Dispose();
                ResultData.ResultCode = ResultStatus.Exception;
                ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][读取串行端口数据]->异常：" + ep.Message;
                return ResultData;
            }
            ResultData.Data = ms.ToArray();
            ms.Dispose();
            return ResultData;
        }

        /// <summary>
        /// 从串口接收数据信息
        /// </summary>
        /// <param name="argAwaitData">是否必须要等待数据返回</param>
        /// <returns>带接收字节的结果对象</returns>
        public ResultModel<byte[]> RreadSerialPortData(bool argAwaitData)
        {
            ResultModel<byte[]> ResultData = new ResultModel<byte[]>() { ResultCode = ResultStatus.Failure };
            MemoryStream ms = new MemoryStream();
            //开始时间，用于确认是否超时的信息
            Thread.Sleep(SleepTime);
            DateTime start = DateTime.Now;
            while (true)
            {
                try
                {
                    byte[] buffer = new byte[this.DeviceSerialPort.BytesToRead];
                    //继续接收数据
                    int sp_receive = this.DeviceSerialPort.Read(buffer, 0, buffer.Length);
                    //存储到流当中
                    ms.Write(buffer, 0, sp_receive);
                    //判断是否读取成功
                    if (this.DeviceSerialPort.BytesToRead < 1)
                    {
                        if ((DateTime.Now - start).TotalMilliseconds > ReceiveTimeout)
                        {
                            ms.Dispose();
                            ResultData.ResultCode = ResultStatus.Failure;
                            ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][读取串行端口数据]->接收数据信息超时";
                            return ResultData;
                        }
                        else if (ms.Length > 0)
                        {
                            break;
                        }
                        else if (argAwaitData)
                        {
                            continue;
                        }
                        else
                        {
                            break;
                        }
                    }
                    Thread.Sleep(SleepTime);
                }
                catch (Exception ep)
                {
                    ms.Dispose();
                    ResultData.ResultCode = ResultStatus.Exception;
                    ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][读取串行端口数据]->异常：" + ep.Message;
                    return ResultData;
                }
            }
            ResultData.Data = ms.ToArray();
            ResultData.ResultCode = ResultStatus.Succeed;
            ms.Dispose();
            return ResultData;
        }

        /// <summary>
        /// 从串口接收数据信息-只适应校验和方式
        /// </summary>
        /// <param name="argSlaveAddress">起始或头</param>
        /// <param name="argStopBit">包尾停止</param>
        /// <param name="argDataLength">数据长度</param>
        /// <returns>带接收字节的结果对象</returns>
        public ResultModel<byte[]> RreadSerialPortData(byte argSlaveAddress, byte argStopBit, byte argDataLength)
        {
            ResultModel<byte[]> ResultData = new ResultModel<byte[]>() { ResultCode = ResultStatus.Failure };
            //是否读取数据
            bool IsReadData = true;
            //是否起始或头是否检查通过
            bool IsSlaveAddressCheck = false;
            //是否长度获取检查通过
            bool IsDataLengthCheck = false;
            //默认长度
            int nDataLength = argDataLength;
            //存储数据集合
            List<byte> DataItem = new List<byte>();
            //等待一段时间再读取状态
            Thread.Sleep(this.SleepTime);
            //开始时间，用于确认是否超时的信息
            DateTime start = DateTime.Now;
            while (IsReadData)
            {
                if ((DateTime.Now - start).TotalMilliseconds <= this.ReceiveTimeout)
                {
                    byte[] buffer = new byte[1024];
                    int sp_receive = 0;
                    try
                    {
                        //继续接收数据
                        sp_receive = this.DeviceSerialPort.Read(buffer, 0, buffer.Length);
                    }
                    catch (Exception) { }
                    //读取到数据,保存到集合中
                    for (int i = 0; i < sp_receive; i++)
                    {
                        DataItem.Add(buffer[i]);
                    }
                    //判断是否有数据
                    if (DataItem.Count > 0)
                    {
                        if (!IsSlaveAddressCheck)
                        {
                            IsSlaveAddressCheck = true;
                            if (DataItem[0] != argSlaveAddress)
                            {
                                IsReadData = false;/*不相等直接退出*/
                                ResultData.ResultCode = ResultStatus.Failure;
                                ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][读取串行端口数据]->接收数据起始头信息错误";
                            }
                        }
                        if (!IsDataLengthCheck)
                        {
                            //获取长度
                            if (IsReadData && DataItem.Count >= this.NodeDataStartNum)
                            {
                                IsDataLengthCheck = true;
                                nDataLength = DataItem[2];
                            }
                        }
                        if (IsReadData && IsDataLengthCheck && DataItem.Count >= nDataLength)
                        {
                            IsReadData = false;
                            ResultData.ResultCode = ResultStatus.Succeed;
                            if (DataItem[DataItem.Count - 1] != argStopBit)
                            {
                                ResultData.ResultCode = ResultStatus.Failure;
                                ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][读取串行端口数据]->接收数据包尾停止信息错误";
                            }
                        }
                    }
                }
                else
                {
                    IsReadData = false;
                    ResultData.ResultCode = ResultStatus.Failure;
                    ResultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][读取串行端口数据]->接收数据信息超时";
                }
            }
            ResultData.Data = DataItem.ToArray();
            return ResultData;
        }

        /// <summary>
        /// 从串口接收数据信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void RreadSerialPortData(object sender, SerialDataReceivedEventArgs e)
        {
            //读取数据
            var resultData = this.RreadSerialPortData();
            //判断是否读取成功
            if (!resultData.ResultState || resultData.Data == null)
            {
                LoggerMgrcore.Instance.WriteDebug($"{this.DevicePara.DeviceName} RreadSerialPortData error ：{resultData.ResultMsg}");
                return;
            }
            //解析数据
            var deviceData = this.ParseDeviceData(resultData.Data);
            if (deviceData == null)
            {
                string strDataMsg = string.Join(" ", Array.ConvertAll(resultData.Data, x => x.ToString("X2")));
                LoggerMgrcore.Instance.WriteDebug($"{this.DevicePara.DeviceName} RreadSerialPortData  ParseDeviceData [{strDataMsg}] is Empty");
                return;
            }
            //广播回调数据
            if (this.DeviceDataNode == null || this.DeviceDataNode.Count <= 0) return;
            foreach (var item in this.DeviceDataNode)
            {
                item.DeviceDataArrive(deviceData);
            }
        }

        /// <summary>
        /// 解析接收字节数据
        /// </summary>
        /// <param name="argBytes"></param>
        /// <returns></returns>
        protected virtual IDeviceData<IDevice<IDevicePara>>? ParseDeviceData(byte[] argBytes)
        {
            return new DeviceDataByte<IDevice<IDevicePara>>((IDevice<IDevicePara>)this, argBytes);
        }

        /// <summary>
        /// 计算校验和[去除头信息]
        /// </summary>
        /// <param name="argDataItem">原字节数组</param>
        /// <returns>校验和字节</returns>
        protected byte CalculateCheckSum(byte[] argDataItem)
        {
            return argDataItem.ToCheckSum();
        }

        /// <summary>
        /// 计算CRC[后两位字节]
        /// </summary>
        /// <param name="argDataItem">原字节数组</param>
        /// <returns>CRC两位字节数组</returns>
        protected byte[] CalculateCheckCRC(byte[] argDataItem)
        {
            return argDataItem.ToCheckCRCModbus();
        }
    }
}
